/**
 *  EKology - An Ecosystem Simulation Framework
 *  Copyright (C) 2008  Aarón Tavío - aaron.tavio at gmail.com - ULPGC
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

package fungusEater.ekosystem;

import fungusEater.*;
import fungusEater.datatypes.*;

import net.ekology.graphics.EKPainter;
import net.ekology.ekosystem.*;
import net.ekology.core.datatypes.*;
import net.ekology.ekosystem.datatypes.*;

import java.util.Calendar;
import java.awt.Graphics2D;
import java.awt.geom.*;
import java.awt.*;
import java.io.*;


/**
 * @author Aarón Tavío - aaron.tavio at gmail.com
 * @version 1.0.0 - 20081019-1700
 */
public class Fungus extends BioticAgent implements Serializable {
    // Attributes
    private Taros oTaros;
    
    private Ellipse2D.Double oBody;
    private Ellipse2D.Double oSpot01,oSpot02,oSpot03;
    private Color oBodyDrawColor;
    private Color oBodyFillColor;
    private Color oSpotDrawColor;
    private Color oSpotFillColor;
    transient private BasicStroke oBorderStroke;
    transient private BasicStroke oInsideStroke;
    private int iTimeBreeding;
    
    
    private double dEnergy;
    private double dWeight;
    
    
    // Features
    private FungusType oType;
    private double dQuality;
    
    
    
    ///////////////////////////////////
    // Initialization methods
    ///////////////////////////////////
    public Fungus() {
        oTaros = null;
        iTimeBreeding = 0;
        
        configAppearence();
    }
    
    public Fungus(String sID, Taros oBiotope, Genome oGenome) {
        super(sID,oBiotope,oGenome);
        oTaros = oBiotope;
        iTimeBreeding = 0;
        
        dEnergy = Config.F_BREED_ENERGY_LOST;
        dWeight = Config.F_WEIGHT_INIT_MIN;
        
        configAppearence();
    }
    
    @Override
    public void initialize() {
        if (getID() == null) { // prevents later calls to initialize()
            System.out.printf("Fungus.java->initialize();\n"); // DEBUG
            oTaros = (Taros) super.oBiotope; // This is neccesary to access Taros' specific methods
            Space oSpace = oTaros.getSpace();

            setID(FungusEaterHelper.generateID());

            /* DEBUG BEGIN
            dEnergy = Config.F_ENERGY_INIT_MIN;
            dWeight = Config.F_WEIGHT_INIT_MIN;

            setPosition(new EKVector(500,0));
            setOrientation(new EKVector(0,1));

            oType = FungusType.B_TYPE;
            dQuality = 1.0;

            oBodyDrawColor = Config.B_TYPE_FUNGUS_COLOR;
              DEBUG END */

            dEnergy = FungusEaterHelper.getRandomValue(Config.F_ENERGY_INIT_MAX,Config.F_ENERGY_INIT_MIN); // Random x value where: 0.6 <= x < 1
            dWeight = FungusEaterHelper.getRandomValue(Config.F_WEIGHT_INIT_MAX,Config.F_WEIGHT_INIT_MIN); // Random x value where: 0.01 <= x < 0.2

            setPosition(FungusEaterHelper.getRandomPosition(oSpace.getDimension(0),oSpace.getDimension(1)));
            setOrientation(FungusEaterHelper.getRandomPosition(oSpace.getDimension(0),oSpace.getDimension(1)));
        }
    }
    
    
    protected void initializeFeatures() {
        Genome oGenome = this.getGenome();
        
        oType = FungusType.valueOf(oGenome.getFeature("oType"));
        dQuality = Double.parseDouble(oGenome.getFeature("dQuality"));
        
        oBodyDrawColor = getDrawColor(oType);
    }
    
    
    private Color getDrawColor(FungusType oType) {
        if (oType == FungusType.A_TYPE) return Config.A_TYPE_ORE_COLOR;
        else if (oType == FungusType.B_TYPE) return Config.B_TYPE_ORE_COLOR;
        else return Config.C_TYPE_ORE_COLOR;
    }
    
    
    
    ///////////////////////////////////
    // Automaton control methods
    ///////////////////////////////////
    @Override
    public int _DO(String sCurrentStateName, int iEKTimeSlice) {
        int iEKTimeUsed;
        
        System.out.printf("Fungus.java->_DO(\"%s\");\n",sCurrentStateName); // DEBUG
        iEKTimeUsed = super._DO(sCurrentStateName, iEKTimeSlice);
        
        printState(); // DEBUG
        
        return iEKTimeUsed;
    }
    
    
    public int juniorResting_DO(int iEKTimeSlice) {
        dEnergy = dEnergy - (Config.F_ENERGY_JUNIOR_DEC * iEKTimeSlice);
        
        return iEKTimeSlice;
    }
    
    
    public int seniorResting_DO(int iEKTimeSlice) {
        if (dEnergy > 0.4) dEnergy = dEnergy - (Config.F_ENERGY_SENIOR_DEC * iEKTimeSlice);
        
        return iEKTimeSlice;
    }
    
    
    public int growing_DO(int iEKTimeSlice) {
        double dNewWeight;
        
        dNewWeight = dWeight + (Config.F_GROW_RATE * iEKTimeSlice);
        dEnergy = dEnergy - ((dNewWeight - dWeight) * Config.F_GROW_ENERGY_FACTOR);
        dWeight = dNewWeight;
        
        return iEKTimeSlice;
    }
    
    
    public int juniorSynthesizing_DO(int iEKTimeSlice) {
        dEnergy = dEnergy + (Config.F_JUNIOR_SYNTH_RATE * iEKTimeSlice);
        
        return iEKTimeSlice;
    }
    
    public int seniorSynthesizing_DO(int iEKTimeSlice) {
        dEnergy = dEnergy + (Config.F_SENIOR_SYNTH_RATE * iEKTimeSlice);
        
        return iEKTimeSlice;
    }
    
    
    public int breeding_DO(int iEKTimeSlice) {
        int iEKTimeUsed;
        
        iTimeBreeding += iEKTimeSlice;
        if (iTimeBreeding >= Config.F_BREED_TIME) {
            
            breed();
            
            iEKTimeUsed = iEKTimeSlice - (iTimeBreeding - Config.F_BREED_TIME);
            iTimeBreeding = 0;
        } else iEKTimeUsed = iEKTimeSlice;

        return iEKTimeUsed;
    }
    
    public int aboutToDie_DO(int iEKTimeSlice) {
        dEnergy=0;
        
        return iEKTimeSlice;
    }
    
    
    
    ///////////////////////////////////
    // Overriden methods
    ///////////////////////////////////
    @Override
    public String[] getAttributeNames() {
        String[] aResult = {"oType","dEnergy","dWeight"};
        
        return aResult;
    }
    
    
    @Override
    public Attribute getAttribute(String sName) {
        Attribute oResult = null;
        
        if (sName.equals("oType")) oResult = new Attribute<FungusType>("oType",oType);
        else if (sName.equals("dEnergy")) oResult = new Attribute<Double>("dEnergy",dEnergy);
        else if (sName.equals("dWeight")) oResult = new Attribute<Double>("dWeight",dWeight);
        
        return oResult;
    }
    
    
    @Override
    public void setAttribute(String sName, String sValue) {
        if (sName.equals("oType")) {
            oType = FungusType.valueOf(sValue.toUpperCase());
            oBodyDrawColor = getDrawColor(oType);
        }
        else if (sName.equals("dEnergy")) dEnergy = Double.parseDouble(sValue);
        else if (sName.equals("dWeight")) dWeight = Double.parseDouble(sValue);
    }
    
    
    public void draw(Graphics2D g2) {
        Area oArea;
        double dXPos,dYPos,dRotation,dScale;
        
        dXPos = getPosition().getComponent(0);
        dYPos = getPosition().getComponent(1);
        dRotation = getOrientation().getDirection();
        dScale = dWeight+0.25;
        
        oArea = new Area(oBody);
        oArea.transform(EKPainter.getTransform(dXPos, dYPos, dRotation, dScale));
        g2.setColor(oBodyDrawColor);
        g2.setStroke(oBorderStroke);
        g2.draw(oArea);
        g2.setColor(oBodyFillColor);
        g2.fill(oArea);
        
        oArea = new Area(oSpot01);
        oArea.transform(EKPainter.getTransform(dXPos, dYPos, dRotation, dScale));
        g2.setStroke(oInsideStroke);
        g2.setColor(oSpotDrawColor);
        g2.draw(oArea);
        g2.setColor(oSpotFillColor);
        g2.fill(oArea);
        
        oArea = new Area(oSpot02);
        oArea.transform(EKPainter.getTransform(dXPos, dYPos, dRotation, dScale));
        g2.setColor(oSpotDrawColor);
        g2.draw(oArea);
        g2.setColor(oSpotFillColor);
        g2.fill(oArea);
        
        oArea = new Area(oSpot03);
        oArea.transform(EKPainter.getTransform(dXPos, dYPos, dRotation, dScale));
        g2.setColor(oSpotDrawColor);
        g2.draw(oArea);
        g2.setColor(oSpotFillColor);
        g2.fill(oArea);
    }
    
    
    
    ///////////////////////////////////
    // Other methods
    ///////////////////////////////////
    public double getEnergy() {
        return dEnergy;
    }
    
    
    public double getWeight() {
        return dWeight;
    }
    
    
    public FungusType getType() {
        return oType;
    }

    
    /**
     * Cuando un hongo recibe una mordida, reduce su tamaño en Config.dBiteSize unidades y devuelve (dQuality*Config.dBiteSize)
     * 
     * @return
     */
    public double incomingBite() {
        double dNewWeight,dWeightLoss;
        
        dNewWeight = dWeight-Config.FE_BITE_SIZE;
        if (dNewWeight<0) dNewWeight = 0;
        dWeightLoss = dWeight - dNewWeight;
        
        dWeight = dNewWeight;
                
        return dQuality*dWeightLoss;
    }
    
    private void printState() {
        System.out.printf("Fungus=[id: '%s', dWeight: '%.3f', dEnergy: '%.3f', Age(SECONDS):%d;]\n",this.getID(),this.dWeight,this.dEnergy,this.getAge(Calendar.SECOND));
    }
    
    private void breed() {
        Genome oNewGenome;
        Fungus oFungus;
        
        System.out.printf("Fungus.java->breed()\n");
        oNewGenome = this.getGenome();
        
        GeneticOperators.mutation(oNewGenome,2,Config.F_FEATURE_MUTATION_PROBABILITY);
        
        oFungus = new Fungus(FungusEaterHelper.generateID(),oTaros,oNewGenome);
        oTaros.fungusBorn(this,oFungus);
        
        dEnergy = dEnergy - Config.F_BREED_ENERGY_LOST;
    }
    
    
    private void configAppearence() {
        oBody = new Ellipse2D.Double(-25,-25,50,50);
        oSpot01 = new Ellipse2D.Double(17,1,5,5);
        oSpot02 = new Ellipse2D.Double(12,-8,8,8);
        oSpot03 = new Ellipse2D.Double(-1,-22,15,12);
        
        // oBodyDrawColor is initialized in initializeFeatures() as it depends on the oType feature
        oBodyFillColor = Color.RED;
        oSpotDrawColor = new Color(0.7f,0f,0f);
        oSpotFillColor = Color.WHITE;
        
        oBorderStroke = new BasicStroke(2f);
        oInsideStroke = new BasicStroke(1f);
    }
    
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        oBorderStroke = new BasicStroke(2f);
        oInsideStroke = new BasicStroke(1f);
    }
    

}
